---
category: Level 3 — Advanced
contributors:
    - jmidtlyng
created: '2020-04-20'
keywords: addEventListener, clientX, clientY, cursor grab, cursor grabbing, drag element, drag scroll, mousedown event, mousemove event, mouseup event, remove CSS property, scrollLeft, scrollTop, set CSS property
title: Drag to scroll
updated: '2021-09-16'
---

User often uses the mouse to scroll in a scrollable container. In addition to that, some applications also allow user to scroll by dragging the element. You can see that feature implemented in a [PDF viewer](https://react-pdf-viewer.dev), [Figma](https://www.figma.com) and many more.

This post shows you a simple way to archive that feature with vanilla JavaScript.

Assume that we have a scrollable container as below:

```html
<div id="container" class="container">...</div>
```

The element must have at least two CSS properties:

```css
.container {
    cursor: grab;
    overflow: auto;
}
```

The `cursor: grab` indicates that the element can be clicked and dragged.

## Scroll to given position

As long as the element is scrollable, we can scroll it to given position by setting the `scrollTop` or `scrollLeft` property:

```js
const ele = document.getElementById('container');
ele.scrollTop = 100;
ele.scrollLeft = 150;
```

## Drag to scroll

The implementation is quite straightforward. By using the similar technique in the [Make a draggable element](https://phuoc.ng/collection/html-dom/make-a-draggable-element/) post, we start with handling the `mousedown` event which occurs when user clicks the element:

```js
let pos = { top: 0, left: 0, x: 0, y: 0 };

const mouseDownHandler = function (e) {
    pos = {
        // The current scroll
        left: ele.scrollLeft,
        top: ele.scrollTop,
        // Get the current mouse position
        x: e.clientX,
        y: e.clientY,
    };

    document.addEventListener('mousemove', mouseMoveHandler);
    document.addEventListener('mouseup', mouseUpHandler);
};
```

> **Tip**
>
> This post uses the [Attach event handlers inside other handlers](https://phuoc.ng/collection/html-dom/attach-event-handlers-inside-other-handlers/) tip

`pos` stores the current scroll and mouse positions. When user moves the mouse, we calculate how far it has been moved, and then scroll to the element to the same position:

```js
const mouseMoveHandler = function (e) {
    // How far the mouse has been moved
    const dx = e.clientX - pos.x;
    const dy = e.clientY - pos.y;

    // Scroll the element
    ele.scrollTop = pos.top - dy;
    ele.scrollLeft = pos.left - dx;
};
```

> **Good practice**
>
> As you see above, the `left`, `top`, `x`, and `y` properties are related to each other.
> It's better to encapsulate them in a single variable `pos` instead of creating four variables.

Last but not least, we can improve the user experience by [setting](https://phuoc.ng/collection/html-dom/set-css-style-for-an-element/) some CSS properties when user starts moving the mouse:

```js
const mouseDownHandler = function(e) {
    // Change the cursor and prevent user from selecting the text
    ele.style.cursor = 'grabbing';
    ele.style.userSelect = 'none';
    ...
};
```

These CSS properties are reset when the mouse is released:

```js
const mouseUpHandler = function () {
    document.removeEventListener('mousemove', mouseMoveHandler);
    document.removeEventListener('mouseup', mouseUpHandler);

    ele.style.cursor = 'grab';
    ele.style.removeProperty('user-select');
};
```

## Use cases

-   [Create a custom scrollbar](https://phuoc.ng/collection/html-dom/create-a-custom-scrollbar/)

Hopefully you love the following demo!

## Demo

<Playground>
```html
<div id="container" style="height: 32rem; overflow: auto; padding: 0 2rem">
    <div class="item">1</div>
    <div class="item">2</div>
    <div class="item">3</div>
    <div class="item">4</div>
    <div class="item">5</div>
    <div class="item">6</div>
    <div class="item">7</div>
    <div class="item">8</div>
    <div class="item">9</div>
    <div class="item">10</div>
</div>
```

```css
.item {
    /* Center the content */
    align-items: center;
    display: flex;
    justify-content: center;

    /* Misc */
    border: 1px solid #cbd5e0;
    font-size: 2.25rem;
    height: 12rem;
    margin: 2rem 0;
    width: 100%;
}
```

```js
document.addEventListener('DOMContentLoaded', function () {
    const ele = document.getElementById('container');
    ele.style.cursor = 'grab';

    let pos = { top: 0, left: 0, x: 0, y: 0 };

    const mouseDownHandler = function (e) {
        ele.style.cursor = 'grabbing';
        ele.style.userSelect = 'none';

        pos = {
            left: ele.scrollLeft,
            top: ele.scrollTop,
            // Get the current mouse position
            x: e.clientX,
            y: e.clientY,
        };

        document.addEventListener('mousemove', mouseMoveHandler);
        document.addEventListener('mouseup', mouseUpHandler);
    };

    const mouseMoveHandler = function (e) {
        // How far the mouse has been moved
        const dx = e.clientX - pos.x;
        const dy = e.clientY - pos.y;

        // Scroll the element
        ele.scrollTop = pos.top - dy;
        ele.scrollLeft = pos.left - dx;
    };

    const mouseUpHandler = function () {
        ele.style.cursor = 'grab';
        ele.style.removeProperty('user-select');

        document.removeEventListener('mousemove', mouseMoveHandler);
        document.removeEventListener('mouseup', mouseUpHandler);
    };

    // Attach the handler
    ele.addEventListener('mousedown', mouseDownHandler);
});
```
</Playground>

## See also

-   [Create a range slider](https://phuoc.ng/collection/html-dom/create-a-range-slider/)
-   [Create an image comparison slider](https://phuoc.ng/collection/html-dom/create-an-image-comparison-slider/)
-   [Create resizable split views](https://phuoc.ng/collection/html-dom/create-resizable-split-views/)
-   [Drag and drop element in a list](https://phuoc.ng/collection/html-dom/drag-and-drop-element-in-a-list/)
-   [Drag and drop table column](https://phuoc.ng/collection/html-dom/drag-and-drop-table-column/)
-   [Drag and drop table row](https://phuoc.ng/collection/html-dom/drag-and-drop-table-row/)
-   [Make a draggable element](https://phuoc.ng/collection/html-dom/make-a-draggable-element/)
-   [Make a resizable element](https://phuoc.ng/collection/html-dom/make-a-resizable-element/)
-   [Resize columns of a table](https://phuoc.ng/collection/html-dom/resize-columns-of-a-table/)
-   [Show a ghost element when dragging an element](https://phuoc.ng/collection/html-dom/show-a-ghost-element-when-dragging-an-element/)
-   [Zoom an image](https://phuoc.ng/collection/html-dom/zoom-an-image/)
